[Dies ist ein Fallbeispiel, das in einem separaten Fenster angezeigt wird. So können Sie das Beispiel und ein beliebiges anderes Hilfethema gleichzeitig betrachten. Das Fenster des Fallbeispiels lässt sich verschieben, in seiner Grösse ändern und über das Schliessfeld verlassen]
Im Mittelpunkt dieses grösseren Programms steht die Gewinnverteilung einer Aktiengesellschaft (AG) nach dem Artikel 671 des schweizerischen Obligationenrechts (OR). Weil das Programm - anders als ein reales Buchhaltungspaket - nicht auf dem laufenden Verkehr der Buchungsperiode aufbauen kann, muss es die Ausgangsdaten der Gewinnverteilung vom Benutzer einlesen. Wir ergänzen deshalb die Objektklassen der Hilfethemen Objekte und Benutzerschnittstelle um Klassen, die benutzerdefinierte Eingabeformulare ermöglichen.
Der Zyklus der doppelten Buchhaltung besteht aus drei Phasen:
Zu Beginn der Buchungsperiode - in der Regel eines Buchhaltungsjahres - wird die Bilanz (Vermögensrechnung) eröffnet.
Während der Buchungsperiode wird der laufende Geschäftsverkehr doppelt verbucht. Jeder Geschäftsfall wir durch einen oder mehrere Buchungssätze festgehalten. Doppelt verbuchen heisst den Sollspalten eines Buchungssatzes gleich viel belasten wie seinen Habenspalten gutschreiben.
Die Buchhaltung einer AG arbeitet mit sechs Erfolgsverteilungskonten:
Das Konto Aktienkapital (AK) enthält die Eigentümeranteile.
Die Erfolgsrechnung (ER) ermittelt den Gewinn oder Verlust.
Das Konto Gewinnverteilung (GV) verteilt den Erfolg. Der Buchungssatz für die Verteilung des Gewinnanteils der Aktionäre heisst zum Beispiel Gewinnverteilung an Dividende. Jede Erfolgsverbuchung ändert den Saldo des Kontos.
Der Reservefonds (Res) häuft den nicht ausgeschütteten Gewinn an.
Dividende (Div) enthält den Gewinnanteil der Aktionäre.
Bei der Präsentation des Beispiels folgen wir den folgenden Entwicklungsphasen:
Entwurf und
Implementation (vgl. auch das Hilfethema Modularisierung).
Der folgende Bildschirmausschnitt beschreibt den Zweck von Gewinnverteilung.xls. Die Beschreibung ist Teil der Eigenschaften des Objekts “Arbeitsmappe Gewinnverteilung.xls” (Menüpunkt »Datei/Eigenschaften). Der Entwickler trägt diese Angaben zu Beginn des Projekts ein.
Dieses Formular erlaubt allerdings nur eine knappe Grobspezifikation des Projekts. Detaillierter fällt eine Feinspezifikation aus. Die folgende Spezifikation geht nach dem EVA-Prinzip des Hilfethemas Modularisierung vor und berücksichtigt die gesetzlichen Vorschriften und die kaufmännische Praxis einer konkreten Gewinnverteilungsaufgabe: Der Artikel 671 des schweizerischen Obligationenrechts und die Gewohnheiten der Branche und des Unternehmens bestimmen, wie der Erfolg auf die erwähnten Gewinnverteilungskonten verteilt wird.
Der Einfachheit halber geben wir konkrete Eingabedaten vor:
|
Beteiligte Konten |
Beispielsaldi |
|
Aufwand (Unternehmungsaufwand) |
150000.- |
|
Ertrag (Unternehmungsertrag) |
180000.- |
|
Aktienkapital (Grundkapital) |
100000.- |
|
Reserve (Reservefonds) |
5000.- |
|
Tantième in % |
10% |
Alle Buchungssätze der Gewinnverteilung werden im Format <Sollkonto> an <Habenkonto>, <Betrag> auf dem Tabellenblatt ausgegeben:
Artikel 671 des schweizerischen Obligationenrechts (OR) regelt die Erfolgsverteilung von Aktiengesellschaften. Die Ziffern des folgenden Gesetztestexts verweisen auf den entwurfssprachlichen Algorithmus. Relative Reservezuweisungen sind unterstrichen:
Aus dem Reingewinn ist jährlich ein Betrag von einem Zwanzigstel einem allgemeinen Reservefonds zuzuweisen (1), bis dieser Fonds die Höhe von einem Fünftel des einbezahlten Grundkapitals erreicht hat (2).
Diesem Reservefonds sind, auch nachdem er die gesetzliche Höhe erreicht hat, zuzuweisen: ...
ein Zehntel derjenigen Beträge (3), die aus dem Reingewinn nach der ordentlichen Speisung des Reservefonds (1) und nach Bezahlung einer Dividende von fünf vom Hundert an Aktionäre (4) und sonstige Gewinnbeteiligte (5) verteilt werden.
Die folgende Regelung ergänzt OR 671. Sie ist gesetzlich nicht bindend, wird aber oft in der Praxis angewendet:
Nach der ordentlichen Reservezuweisung (1) erhält der Verwaltungsrat eine Tantième in % des Gewinns (5). Nach der zweiten Speisung der Reserve (3) sind vom Gewinnrest soviele ganze Prozent vom einbezahlten Grundkapital wie möglich als Zusatzdividende auszuschütten (6).
Wer die Gewinnverteilung nach OR 671 programmieren möchte, steht vor einem Problem, das typisch für die Softwareentwicklung ist: Verbale Spezifikationen sind selten eindeutig. Die Interpretation des obigen Gesetztestext ist zum Beispiel aus den folgenden Gründen schwierig:
- Der Reingewinn (Nettogewinn oder kurz Gewinn) ist die Differenz von Ertrag und Aufwand.
- Der Reservefonds enthält jenen Teil des Gewinns, der nicht als Dividende an Aktionäre oder als Tantième an die Verwaltungsräte verteilt wird.
- Das Grundkapital (Aktienkapital) ist Berechnungsbasis der Dividende.
- Eine erste Zuweisung an die Aktionäre heisst Grunddividende, solange sie einen Fünftel desAktienkapitals nicht überschreitet. Was darüber hinaus geht, heisst Zusatzdividende (Superdividende). Im Gegensatz zur Grunddividende verlangt eine Zusatzdividende eine Reservezuweisung.
Die OR 671 ergänzende Praxisregelung schreibt vor, dass vom Gewinnrest soviele ganze Prozent Zusatzdividende wie möglich auszuschütten seien. Diese Ausschüttung erfordert eine weitere Reservezuweisung von 0.1*Zusatzdividende. Die Zusatzdividende berechnet sich in drei Schritten:
Wir berechnen zuerst die genauen Prozente des Aktienkapitals, die man aus dem Saldo des Kontos Gewinnverteilung noch als Zusatzdividende ausschütten könnte.
Dann bestimmen wir den ganzzahligen Anteil des Ergebnisses.
Der genaue Zusatzdividendensatz ergibt sich aus den folgenden Gleichungen:
Zusatzdividende = ZusatzdivSatz * 0.01AK (1) ZusatzdivSatz = (GV - ResAufZusatzdiv) / 0.01AK (2) ResAufZusatzdiv = 0.1 * ZusatzdivSatz * 0.01AK (3)
(2) berechnet den genauen Zusatzdividendensatz, der sich nach Abzug der Reservenzuweisung ResAufZusatzdiv noch aus dem Gewinnrest GV ausschütten lässt. (3) formalisiert die Reservevorschrift von OR 671.
Einsetzen von (3) in (2) ergibt den Zusatzdividendensatz:
ZusatzdivSatz = (GV - 0.1 * ZusatzdivSatz * 0.01AK) / 0.01 AK ZusatzdivSatz = GV / 0.01 AK - 0.1 * ZusatzdivSatz 1.1 ZusatzdivSatz = GV / 0.01AK ZusatzdivSatz = GV / 0.01AK / 1.1 = GV / 0.011AK.
Im zweiten Schritt bestimmen wir den ganzzahligen Anteil des Zusatzdividendensatzes. Die VBA-Funktion Int(eger) liefert den ganzzahligen Anteil ihres Arguments; Int(99.8) ergibt zum Beispiel 99. Den ganzzahligen Zusatzdividendensatz berechnen wir deshalb als Int(GV / 0.011AK). Die Formel (1) zur Berechnung der Zusatzdividende lautet dann:
Zusatzdividende = Int(GV / 0.011AK) * 0.01AK.
Die Arbeitsmappe Gewinnverteilung.xls setzt sich aus den GUI-Objekten Tabellenblatt und Eingabeformular sowie dem Algorithmus der Gewinnverteilung zusammen:
Tabellenblatt Dialogblatt
Formular Eingabeformular (engl. user form)
Modul Verarbeitungsmodul
Bevor wir die Gewinnverteilung als VBA implementieren, formulieren wir eine erste entwurfssprachliche Annäherung an den Kernalgorithmus. Dabei verwenden wir die folgenden Abkürzungen (Kommentare stehen vor Hochkommas und sind grün):
'ER Erfolgsrechnung 'GV Gewinnverteilung 'AK Aktienkapital 'T% Tantième in % 'kursiv BuchungssätzeFalls Aufwand < Ertrag dann 'Gewinn Gewinn = Ertrag - Aufwand ER an GV, Gewinn Falls Reserve < AK / 5 dann '(2) GV an Reserve, Gewinn / 20 '(1) GV an Dividende, Aktienkapital / 20 '(4) GV an Tantième, Gewinn * T% / 100 '(5) GV an Reserve, Gewinn * T% / 100 / 10 '(3) ZusatzDivSatz = GV / 0.011AK Zusatzdividende = AK * Int (ZusatzDivSatz) / 100 GV an Dividende, Zusatzdividende '(5,6) GV an Reserve, Zusatzdividende / 10 '(3) Falls GV > 0 dann GV an Reserve, GV sonst 'Verlust Verlust = Aufwand - Ertrag Falls Verlust < Reserve dann Reserve an ER, Verlust sonst Reserve an ER, Reserve GV an ER, Verlust - ReserveGewinnverteilung gemäss OR 671 und Praxis (erste Annäherung)
Wer diesen Algorithmus mit der Subroutine Buchungssätze()der Arbeitsmappe Gewinnverteilung.xls vergleicht, stösst auf Unterschiede. Die programmiersprachliche Version prüft nämlich noch für jeden Buchungssatz, ob der laufende Saldo des Gewinnverteilungskontos für die Buchung ausreicht. Um das Verständnis zu erleichtern lässt der obige Entwurf diese Prüfung weg. Er skizziert nur den algorithmischen Kern.
Der grösste Teil des Code von Gewinnverteilung.xls widmet sich der Benutzeroberfläche. Das folgende Bild zeigt auf dem Hintergrund die vier Spalten des Tabellenblatts Dialogblatt. Die Ausgabeanweisung Range(Zelle).Value = Wert schreibt jeden vom Programm ermittelten Wert in die passende Zelle dieses Tabellenblatts. Ein Klick auf die Schaltflläche Start ruft die Ereignisprozedur Start_Click(), welche das Eingabeformular anzeigt. Auf dem farbigen Hintergrund des Tabellenblatts sehen Sie ...

Wie ein Tabellenblatt stellt auch ein benutzerdefiniertes Formular Objekte, Eigenschaften und Methoden bereit. Das obige Eingabeformular Falldaten eingeben enthält für jede Eingabe ein Bezeichnungsfeld und ein Textfeld (weisses Feld rechts vom Bezeichnungsfeld). Das programmtechnische Lesen eines Textfelds ist einfach: die Nennung des Namens - zum Beispiel Aufwand - genügt. VBA vervollständigt Aufwand automatisch zu Aufwand.Value, wobei Value jene Eigenschaft ist, welche den laufenden Wert des Felds enthält.
Das nächste Bild zeigt den Entwurf des oben gezeigten Eingabeformulars. Der Entwurf verläuft in vier Schritten:
1. Entwicklungsumgebung aufrufen (»Alt/F11)
2. Leeren Formularrahmen zeichnen
3. Steuerlemente in den Formularrahmen setzen (Bezeichnungs- und Textfelder; Schaltflächen)
4. Den Steuerelementen Ereignisprozeduren zuordnen (abbrechen_Click, verbuchen_Click).
Das entworfene Formular lässt sich mit Show anzeigen und mit Hide vorübergehend ausblenden.

Entwurf des Eingabeformulars "Falldaten eingeben"
Nach dem Entwurf des algorithmischen Kerns und der Benutzeroberfläche bereiten wir im nächsten Entwurfscode die Programmierung in VBA vor. Elemente des Objektmodells von MS Excel sind blau, Schaltflächen stehen zwischen Hochkommas und Ereignisprozeduren sind rot. Für die Subroutine Buchungssätze() gilt dasselbe wie für den Kernalgorithmus: Sie unterscheidet sich von der programmiersprachlichen Variante, weil sie nicht für jeden Buchungssatz kontrolliert, ob der Saldo des Kontos Gewinnverteilung für die jeweilige Buchung noch ausreicht.
Ereignisprozedur Start_Click() Ausgabebereich von Dialogblatt löschen Eingabeformular mit ‘Abbrechen’ und ‘Verbuchen’ anzeigen Falls abgebrochen <> True dann Buchungssätze()Ereignisprozedur abbrechen_Click() Eingabeformular nach Klick auf ‘Abbrechen’ schliessen abgebrochen = TrueEreignisprozedur verbuchen_Click() ‘--- Leere und nichtnumerische Eingaben verhindern Für jedes Feld von Eingabeformular Falls leer oder nichtnumerisch dann Formular nach Klick auf ‘Verbuchen’ nicht schliessen Fehlermeldung zeigen Kontrolle an den Benutzer zurückgebenSubroutine Buchungssätze() Eingabeformular lesen (Aufwand, Ertrag, Aktienkapital, Reserve, Tantième in %) '-- Buchungssätze gemäss OR 671 auf Dialogblatt schreiben Falls Gewinn dann BS "ER", "GV", Gewinn 'Reingewinn Falls Reserve < AK dann BS "GV", "Reserve", Gewinn / 20 BS "GV", "Dividende", Aktienkapital / 20 BS "GV", "Reserve", Gewinn / 20 / 10 BS "GV", "Tantieme", Gewinn * Tantiemensatz / 100 BS "GV", "Reserve", Gewinn * Tantiemensatz / 100 / 10 BS "GV", "Dividende", AK * Int(GV / 0.011AK) / 100 BS "GV", "Reserve", AK * Int(GV / 0.011AK) / 100 / 10 Falls GV < 0 dann "GV", "Reserve", GV sonst ‘Verlust Falls Verlust < Reserve DANN BS "Reserve", "ER", Verlust sonst BS "Reserve", "ER", Reserve BS "GV", "ER", Verlust - ReserveSubroutine BS(Sollkonto, Habenkonto, Buchungsbetrag) Buchungssatz auf Dialogblatt schreiben Konto Gewinnverteilung nachführenProzeduraler Entwurf (zweite Annäherung. Objekt, Ereignisprozedur, ‘Schaltfläche’)
Wir betrachten den VBA-Code in drei Schritten:
Wir unterscheiden zwischen Variablen, die für das ganze Projekt, für ein Modul oder nur eine Prozedur gelten und erklären die Funktion der Ereignisprozedur Start_Click des Tabellenblatts.
Wir betrachten die übrigen Ereignisprozeduren des Eingabeformulars.
Der folgende Code-Abschnitt deklariert zu Beginn die Variable abgebrochen als öffentlich (engl. Public), damit sie im ganzen Projekt, das heisst sowohl im Modul Dialogblatt als auch im Modul Eingabeformular, verwendet werden kann. Ausgabezeile und GV sind hingegen Private-Variablen, weil sie je nur in einem einzigen Modul vorkommen.
' --- Public für alle Module sichtbar
Public abgebrochen As Boolean ' True, falls “Abbrechen”
' --- Private nur für das Verarbeitungsmodul sichtbar
Private Ausgabezeile As Integer
Private GV As Currency ' Konto Gewinnverteilung
Sub Start_Click()
' --- Dialogblatt und Eingabeformular initialisieren
GV = 0
abgebrochen = False
' Ausgabebereich des Dialogblatts löschen
Dialogblatt.Range("A4:F11").ClearContents
' Eingabeformular die Kontrolle übergeben
Eingabeformular.Show ' laden und anzeigen
' --- Gefülltes Eingabeformular verarbeiten
If abgebrochen = False Then
Buchungssätze
Unload Eingabeformular ' aus Speicher entfernen
End If
End Sub
Eine Variable, die nur in jener Einheit (Modul oder Prozedur) sichtbar ist, in der sie vereinbart worden ist, heisst lokal. Der Programmierer darf zum Beispiel eine private Variable nur in jenem Modul verwenden, in dem er sie eingeführt hat. Wenn er hingegen eine Variable als Public vereinbart, dann darf er sie auch ausserhalb des deklarierenden Moduls ansprechen. Variablen, die auch ausserhalb der deklarierenden Einheit gelten, heissen global.
Die Schlüsselwörter Public und Private werden selten gebraucht. Häufiger ist das Schlüsselwort Dim. Es dient der Vereinbarung einer Variablen innerhalb einer einzigen Prozedur. Man sagt auch, der Gültigkeitsbereich einer Dim-Variablen beschränke sich auf die Prozedur. Private deklariert hingegen Variablen, die für alle Prozeduren eines Moduls gelten. Public macht sogar eine Vereinbarung für alle Module eines Projekts - zum Beispiel aller Module des Projekts Gewinnverteilung - sichtbar.
Die Ereignisprozedur Start_Click() des obigen Programmausschnitts lädt und zeigt das Eingabeformular mit dem Befehl Eingabeformular.Show. Die Codezeile Unload Eingabeformular entfernt das Formular-Steuerelement erst, nachdem der Benutzer die Dateneingabe regulär abgeschlossen hat oder die Schaltfläche Abbrechen geklickt hat. Das Programm erkennt am Wert True der Public-Variable abgebrochen, dass der Benutzer das Eingabeformular irregulär verlassen hat.
Der nächste VBA-Ausschnitt enthält die Ereignisprozeduren der beiden Schaltflächen Abbrechen und Verbuchen. Verbuchen_Click() prüft nur die Gültigkeit der Daten des Eingabeformulars. Den Aufruf der Kernprozedur Buchungssätze finden Sie in der Steuerprozedur Start_Click().
'Modul Eingabeformular
'---------------------
Private Sub Abbrechen_Click()
abgebrochen = True
Unload Eingabeformular
End Sub
Private Sub Verbuchen_Click()
'Eingabeformular nach Klick auf "Verbuchen" prüfen
'Aufwand, etc. sind Textfelder des Eingabeformulars
'Aufwand="" bedeutet dasselbe wie Aufwand.Value=""
If Aufwand = "" Or _
Ertrag = "" Or _
Aktienkapital = "" Or _
Reserve = "" Or _
TantièmeInProzent = ""
Then
MsgBox "Keine Leereingaben möglich"
ElseIf Not (IsNumeric(Aufwand) And _
IsNumeric(Ertrag) And _
IsNumeric(Aktienkapital) And _
IsNumeric(Reserve) And _
IsNumeric(TantièmeInProzent))
Then
MsgBox "Nur numerische Eingaben möglich"
Else
Eingabeformular.Hide 'vorübergehend ausblenden
End If
End Sub
Gewinnverteilung.xls
ist komplexer als andere Arbeitsmappen ...
weil die Problemstellung der Gewinnverteilung von Aktiengesellschaften schwieriger ist und
weil die Benutzeroberfläche neben einem Tabellenblatt und den bereits bekannten vordefinierten Dialogfeldern ein benutzerdefiniertes Eingabeformular mit sieben Steuerelementen umfasst.
Wenn ein Projekt umfangreich ist, lohnt es sich, auf die einzelnen Objekte aus dem Objektkatalog (engl. object browser, Menüpunkt »Ansicht/Objektkatalog) zuzugreifen. Der Objektkatalog zeigt die Objektklassen, Eigenschaften, Methoden, Ereignisse und Konstanten benutzerdefinierter Projekte und vordefinierter Objektbibliotheken von Softwareanbietern.
Der Objektkatalog erleichtert insbesondere den Zugriff auf benutzerdefinierte Objekte. Der folgende Bildschirmausschnitt greift zum Beispiel auf unser Projekt Gewinnverteilung zu. Es besteht aus den Projektkomponenten Arbeitsmappe, Dialogblatt (Tabellenblatt), Eingabeformular und Verarbeitungsmodul. Unter <global> zeigt der Objektkatalog alle Public-Vereinbarungen. Im Bildschirmausschnitt liegt der Cursor auf dem Verarbeitungsmodul. Weil dieses benutzerdefiniert ist, zeigt das rechte Fenster die globale Variable abgebrochen, die lokalen Variablen Ausgabezeile und GV sowie die Methoden BS, Buchungssätze und Start_Click. Im rechten Fenster ist die Methode BS markiert; das untere Fenster zeigt deshalb den Kopf der Prozedur BS.
Neben benutzerdefinierten Projekten können Sie vordefinierte Objektmodelle inspizieren, im besonderen jene von MS Excel und VBA. Der nächste Bildausschnitt positioniert zum Beispiel auf der Eigenschaft Cells des Objekts Worksheet des Objektmodells von MS Excel. Das unterste Fenster gibt an, dass Cells eine Eigenschaft eines Tabellenblatts (engl. worksheet) ist, die ein Range-Objekt mit dem Zellbereich des ganzen Tabellenblatts ergibt.